Building Finalizable and Disposable Objects

At this point, we have seen two different approaches to constructing a class that cleans up internal unmanaged resources. On the one hand, you can override System.Object.Finalize(). Using this technique, you have the peace of mind that comes with knowing the object cleans itself up when garbage-collected (whenever that may be) without the need for user interaction. On the other hand, you can implement IDisposable to provide a way for the object user to clean up the object as soon as it is finished. However, if the caller forgets to call Dispose(), the unmanaged resources may be held in memory indefinitely.

As you might suspect, it is possible to blend both techniques into a single class definition. By doing so, you gain the best of both models. If the object user does remember to call Dispose(), you can inform the garbage collector to bypass the finalization process by calling GC.SuppressFinalize(). If the object user forgets to call Dispose(), the object will eventually be finalized and have a chance to free up the internal resources. The good news is that the object’s internal unmanaged resources will be freed one way or another.

Here is the next iteration of MyResourceWrapper, which is now finalizable and disposable, defined in a C# Console Application named FinalizableDisposableClass:

// A sophisticated resource wrapper.
public class MyResourceWrapper : IDisposable
{
    // The garbage collector will call this method if the
    // object user forgets to call Dispose().
    ~MyResourceWrapper()
    {
        // Clean up any internal unmanaged resources.
        // Do **not** call Dispose() on any managed objects.
    }

    // The object user will call this method to clean up
    // resources ASAP.
    public void Dispose()
    {
        // Clean up unmanaged resources here.
        // Call Dispose() on other contained disposable objects.
        
        // No need to finalize if user called Dispose(),
        // so suppress finalization.
        GC.SuppressFinalize(this);
    }
}

Notice that this Dispose() method has been updated to call GC.SuppressFinalize(), which informs the CLR that it is no longer necessary to call the destructor when this object is garbage-collected, given that the unmanaged resources have already been freed via the Dispose() logic.

A Formalized Disposal Pattern

The current implementation of MyResourceWrapper does work fairly well; however, we are left with a few minor drawbacks. First, the Finalize() and Dispose() methods each have to clean up the same unmanaged resources. This could result in duplicate code, which can easily become a nightmare to maintain. Ideally, you would define a private helper function that is called by either method.

Next, you’d like to make sure that the Finalize() method does not attempt to dispose of any managed objects, while the Dispose() method should do so. Finally, you’d also like to be certain the object user can safely call Dispose() multiple times without error. Currently, our Dispose() method has no such safeguards.

To address these design issues, Microsoft defined a formal, prim-and-proper disposal pattern that strikes a balance between robustness, maintainability, and performance. Here is the final (and annotated) version of MyResourceWrapper, which makes use of this official pattern:

class MyResourceWrapper : IDisposable
{
    // Used to determine if Dispose()
    // has already been called.
    private bool disposed = false;

    public void Dispose()
    {
        // Call our helper method.
        // Specifying "true" signifies that
        // the object user triggered the cleanup.
        CleanUp(true);

        // Now suppress finalization.
        GC.SuppressFinalize(this);
    }

    private void CleanUp(bool disposing)
    {
        // Be sure we have not already been disposed!
        if (!this.disposed)
        {
            // If disposing equals true, dispose all
            // managed resources.
            if (disposing)
            {
                // Dispose managed resources.
            }
            // Clean up unmanaged resources here.
        }
        disposed = true;
    }

    ~MyResourceWrapper()
    {
        // Call our helper method.
        // Specifying "false" signifies that
        // the GC triggered the cleanup.
        CleanUp(false);
    }
}

Notice that MyResourceWrapper now defines a private helper method named CleanUp(). By specifying true as an argument, we indicate that the object user has initiated the cleanup, so we should clean up all managed and unmanaged resources. However, when the garbage collector initiates the cleanup, we specify false when calling CleanUp() to ensure that internal disposable objects are not disposed (as we can’t assume they are still in memory!). Last but not least, our bool member variable (disposed) is set to true before exiting CleanUp() to ensure that Dispose() can be called numerous times without error.

Note After an object has been “disposed”, it’s still possible for the client to invoke members on it as it is still in memory. Therefore, a robust resource wrapper class would also need to update each member of the class with additional coding logic that says, in effect, “if I am disposed, do nothing and return from the member.”

To test our final iteration of MyResourceWrapper, add a call to Console.Beep() within the scope of your finalizer:

~MyResourceWrapper()
{
    Console.Beep();
    // Call our helper method.
    // Specifying "false" signifies that
    // the GC triggered the cleanup.
    CleanUp(false);
}

Next, update Main() as follows:

static void Main(string[] args)
{
    Console.WriteLine("***** Dispose() / Destructor Combo Platter *****");

    // Call Dispose() manually, this will not call the finalizer.
    MyResourceWrapper rw = new MyResourceWrapper();
    rw.Dispose();

    // Don't call Dispose(), this will trigger the finalizer
    // and cause a beep.
    MyResourceWrapper rw2 = new MyResourceWrapper();
}

Notice that we are explicitly calling Dispose() on the rw object, so the destructor call is suppressed. However, we have "forgotten" to call Dispose() on the rw2 object, and therefore, when the application terminates, we hear a single beep. If you were to comment out the call to Dispose() on the rw object, you would hear two beeps.

Source Code The FinalizableDisposableClass project is included under the Chapter 8 subdirectory.

That concludes our investigation of how the CLR manages your objects via garbage collection. While there are additional (somewhat esoteric) details regarding the collection process I haven’t covered here (such as weak references and object resurrection), you are now in a perfect position for further exploration on your own. To wrap this chapter up, we will examine a brand new .NET 4.0 programming feature called “lazy instantiation” of objects.